const Controller = require('egg').Controller;

const nowTime = Date.now();
const Web3 = require('web3');
const HDWalletProvider = require('truffle-hdwallet-provider');
const Decimal = require('decimal.js');
const BigNumber = require('bignumber.js');
// var web3 = new Web3();
// web3.setProvider(new Web3.providers.HttpProvider("https://rinkeby.infura.io/v3/ef8cd40931114dabb4032fc0314a4d55"));
const mnemonic = "winner weird snack cry today penalty isolate joy secret turn jealous wagon"
const url = "https://rinkeby.infura.io/v3/ef8cd40931114dabb4032fc0314a4d55";
const web3 = new Web3(new HDWalletProvider(mnemonic, url));

const timeout = 5;
function toInt(str) {
    if (typeof str === 'number') return str;
    if (!str) return str;
    return parseInt(str, 10) || 0;
}
// 创建订单
const createRule = {
    uuid: { type: 'string' },
    actionSelector: { type: 'string' },
    userTimeStamp: { type: 'string' },
    amount: { type: 'string' },
    sign: { type: 'string' },
    actionAddress: { type: 'string' },
    userAddress: { type: 'string' },
    isBuy: { type: 'string' }
};
// 提现
const cashRule = {
    uuid: { type: 'string' },
    actionSelector: { type: 'string' },
    // parameter:  { type: 'string' },
    userTimeStamp: { type: 'string' },
    // srvTimestamp: { type: 'string' },
    amount: { type: 'string' },
    sign: { type: 'string' },
    // actionAddress: { type: 'string' },
    // accountId: { type: 'string' },
    // blockNum: { type: 'string' },
    cash: { type: 'string' }
};
const listRule = {
    userAddress: {type: 'string'}
};

// 准备 定时发送用的
const sendRule = {
    ifFail: {type: 'string'}
};
// 合约rest
const contractRule = {
    accountAddr: {type: 'string'},
    actionCtrAddr: {type: 'string'},
    parameter: {type: 'string'},
    startTime: {type: 'string'},
    endTime: {type: 'string'},
};
// 前段买涨买跌统计
const isBuyCountRule = {
    openTime: {type: 'string'},
    closeTime: {type: 'string'},
}


class ActionController extends Controller {
    /**
     * 前端列表显示
     * 参数：limit offset userAddress 
     */
    async index() {
        // const ctx = this.ctx;
        const { ctx, service } = this;
        ctx.status = 200;
        ctx.body = await service.actions.index();
    }
    /**
     * 冻结余额 = 下单冻结 + 提现冻结
     * 参数 userAddress
     */ 
    async blockedBalance() {
        // const ctx = this.ctx;
        const { ctx, service } = this;
        ctx.status = 200;
        let res = await service.actions.blockedBalance();
        if(res.status_code == '422') {
            ctx.status = 422; 
        }else {
            ctx.status = 200; 
        }
        ctx.body = res;
        return;
    }
    /** 
     * 当前买涨/买跌的数量(前端)
     * 涨1 | 跌2
     * */ 
    async isBuyCount() {
        const { ctx, service } = this;
        ctx.status = 200;
        ctx.body = await service.actions.isBuyCount();
        return;
    }

    /**
     * 下订单和提现 在一起
     * 1、验证提交的数据是否为空
     * 2、验证用户是否存在 // 下面验证了签名这一步就不用了
     * 3、验证随机数是否重复
     * 4、下单是否超时
     * 5、从accounts 表根据userAddress拿到account id 赋值给 actions 表的accountId
     * 5、签名是否是这个用户
     * 6、用户合约余额 >= 本地balance - freezeBalance
     * 6、accounts余额什么的都够，生成订单的时候报错用了事务
     * 7、看看这一轮是否同时下了买涨|买跌，下的涨也下了跌那就是isMore 就是true(1)
     * ------------------ 有这个符号的都是需要去合约拿值的 ------------------
     */ 
    async create() {

        // const ctx = this.ctx;
        const { ctx, service } = this;
        // console.log(await service.abi.WithDraw_CtrAddr())
        // ctx.body = await service.abi.WithDraw_CtrAddr();
        // return;

        // web3 = new Web3(new HDWalletProvider(mnemonic, url));
        // 提现 await service.actions.
        let contract = new web3.eth.Contract(await service.abi.WithDraw_ABI(), await service.abi.WithDraw_CtrAddr());

        // ctx.request.body.amount =  web3.utils.fromWei(amount, 'ether');//amount;
        let left = ctx.request.body.actionSelector.split("(");
        let right = left[1].split(")");
        let actionSelectorArr = right[0].split(",");
        actionSelectorArr = ['string', actionSelectorArr[1], actionSelectorArr[2], actionSelectorArr[3], actionSelectorArr[4], actionSelectorArr[5]];
        
        actionSelectorArr = actionSelectorArr.splice(0,4)
        
        let payloadObj = web3.eth.abi.decodeParameters(actionSelectorArr, ctx.request.body.payload);
        // uuid , actionSelect , amount, isBUy
        let uuid = payloadObj[0];
        let actionSelector = payloadObj[1];
        // 判断是否与传过来的不一致
        if(ctx.request.body.actionSelector == actionSelector) {
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': 'actionSelector 数据类型不一致！'};
            return;
        }
        let amount = payloadObj[2];
        let isBuy = payloadObj[3];
        // let uuid = 3;
        // let actionSelect = 111;
        // let amount = "1000";
        // let isBuy = 1;
        let sign = ctx.request.body.sign;
        let userAddress = ctx.request.body.userAddress;
        let UserTimeStamp = ctx.request.body.UserTimeStamp;
        let data = ctx.request.body.payload; // 

        ctx.request.body.encodeParameters = data;
        ctx.request.body.sign = sign;
        ctx.request.body.uuid = uuid;
        ctx.request.body.isBuy = isBuy;

        ctx.request.body.amount =  web3.utils.fromWei(amount, 'ether');//amount;
        // ctx.body = ctx.request.body;
        // return;

        ctx.request.body.srvTimestamp = Date.now();
        ctx.request.body.actionSelector = actionSelector;
        ctx.request.body.UserTimeStamp = UserTimeStamp;

        const sign_userAddress = await web3.eth.personal.ecRecover(ctx.request.body.payload, sign);
        
        const query_acount_userAddressIsset = {
            where: {
                userAddress: userAddress
            }
        };
        const account_userAddress_isset = await ctx.model.Account.findAll(query_acount_userAddressIsset);
        
        // validate account/userAddress isset 
        if(account_userAddress_isset.length == 0) {
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': '用户不存在，请不要非法操作！'};
            return;
        };
       
        // validate userAddress 
        if(sign_userAddress != userAddress.toLowerCase()) {
            ctx.status = 422;
            ctx.body = {'status_code':502, 'msg': '用户不一致！'};
            return;
        }
        
        // validate uuid required
        const query_actionUuidRequired = {
            where: {
                uuid: uuid
            }
        };
        const uuidRequired = await ctx.model.Action.findAll(query_actionUuidRequired);
        
        if(uuidRequired.length != 0) {
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': '随机数已存在'};
            return;
        }
        // 获取account  id 插入 action/account_id
        let accountRow = await ctx.model.Account.findOne({
            where: {
                userAddress: ctx.request.body.userAddress
            }
        });
        
        // ------------------------------其次就是获取合约用户的balance 和本地对比------------------------------
        // const contractBalance = "9899958.9999999999999997"; // 合约的

        // const balanceZero = new BigNumber(accountRow.balance).minus(new BigNumber(accountRow.freezeBalance)).minus(new BigNumber(contractBalance))>0;

        // if(balanceZero) {
        //     ctx.status = 422;
        //     ctx.body =  {'status_code':422, 'msg': "金额有问题，请联系我们！"};
        //     return; 
        // }

        ctx.request.body.accountId = accountRow.id; 
        
        let accounts_freezeAmount = ctx.request.body.amount;
        
        let accounts_userAddress = ctx.request.body.userAddress;
        const conn = await this.app.mysql.beginTransaction();
        // 下单的金额够不够用 不够直接catch，够了then的时候形成订单失败也会回滚 
        return await conn.query(`UPDATE accounts SET freezeBalance=freezeBalance+${accounts_freezeAmount} WHERE userAddress='${accounts_userAddress}' and freezeBalance<=balance-${accounts_freezeAmount}`).then( async result => {
           
            if(ctx.request.body.cash == 'cash') {
                ctx.request.body.isBuy = 3;
            }

            const { uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters } = ctx.request.body;
            const action = await ctx.model.Action.create({ uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters });
            
            if(ctx.request.body.cash == 'cash') {
                let accountRow = await ctx.model.Account.findById(action.accountId);
                let uuid = action.uuid;
                let userAddr = accountRow.userAddress;
                let withdrawAmount = action.amount;
                let sign = action.sign;
                // 合约的
                let accounts = await web3.eth.getAccounts();
                let result = await contract.methods.ActionCallBack_WithDraw(uuid,userAddr,withdrawAmount,sign).send({
                    from: accounts[0],
                })
                console.log(result)
                // 发东西给合约,返回的值,看看有没有某个值 然后修改.没有是要回滚的
                // { uuid_c, addr_c, amount_c, sign_c }
                
                // -------------------创建完成，把数据提交给合约---------------------------------
                // let constractUuid = "uuid"; // 这个应该是合约返回的值
                // const ActionCastRes = await ctx.model.Action.findOne({
                //     where: {
                //         uuid: constractUuid 
                //     }
                // });
                // await conn.commit(); // 提交事务才是成功
                // await ActionCastRes.update({ ifFail:1 }); // ifFail 1 成功， 0没有提交 2 失败
                // ctx.status = 201;
                // ctx.body = {'status_code':201, 'msg': "提现成功!"};
            }else {  
                await conn.commit(); // 提交事务
                ctx.status = 201;
                ctx.body = {'status_code':201, 'msg': action};
            }
            
        }).catch(async err => {
            ctx.status = 422;
            await conn.rollback(); // 一定记得捕获异常后回滚事务！！
            ctx.body = {'status_code':422, 'msg': err};
        });
        // ctx.body = sign_userAddress;
        // return;
    }
  
    // ifFail 成功1 | 失败0 失败的要再次发送 ，isSettled 是否已结算 未结算0 | 已结算1
    async sendContract() {
        const ctx = this.ctx;
        //
        // ifFail 就是发送失败或者没有发送的
        const query = {
            where: {
                srvTimestamp: {
                    $between: [toInt(ctx.query.startTime),toInt(ctx.query.endTime)]
                }
            }     
        };

        const actions = await ctx.model.Action.findAll(query);
        
        // 数据库没有一条符合条件的轮询，直接结束
        if (actions.length == 0) {
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': '没有单子要传！'};
            return;
        }
        // 
        let web3 = new Web3(new HDWalletProvider(mnemonic, url));
        // ctx.status = 201;
        // ctx.body = actions;
        // return;
        let contract2 = new web3.eth.Contract(await service.abi.Order_ABI(), await service.abi.Order_CtrAddr());
        // console.log(contract2.methods)
        actions.forEach(async function(row)  {
            let nonce = 0;
            // 只有超过指定时间才发送到前段去
            // 获取用户地址
            const accountRow = await ctx.model.Account.findOne({
                where: {
                    id: 8
                }
            });
            let uuid = "0xfea3eaa111b347e621dbfe36b695e5f1b8de87d25614bde36554a296a32410";
            let userAddr = accountRow.userAddress;
            let tradeAmount = "1000000000000000000000000";
            let isBuy = true;
            let srvTimestamp = row.srvTimestamp;
            let sign = row.sign;
            let accounts = await web3.eth.getAccounts();
            let result1 = await contract2.methods.Nosign_ActionCallBack_SettleOrder(uuid,userAddr,tradeAmount,isBuy, srvTimestamp,sign).send({
                from: accounts[0],
                gas:"5000000",
            })
                
        });
        ctx.status = 201;
        ctx.body = 1111;


    }

    /**
     * 智能合约rest
     * 先判断 accounts 是否有这个 accountAddr 有形成一个数组，然后判断actions 是否在这个数组中
     *  */
  
    async contractRes() {
        const ctx = this.ctx;
        
        ctx.validate(contractRule, ctx.query);
        let actionRes = await ctx.model.Account.findAll({
            where: {
                accountAddress: ctx.query.accountAddr
            }
        });
       
        // 符合 accountAddr 存一个数组
        let accountIdArr = [];
        actionRes.forEach(async function (row) {
            accountIdArr.push(row.id);
        });
        
        // 把发送失败的要再次去发送的
        let ifFail = 0;
        // 
        const query = {
            where: {
                actionAddress: ctx.query.actionCtrAddr,
                parameter: ctx.query.parameter,
                ifFail: ifFail,  
                accountId: accountIdArr,
                srvTimestamp: {
                    $between: [toInt(ctx.query.startTime),toInt(ctx.query.endTime)]
                }
            },
        };

        const actions = await ctx.model.Action.findAll(query);
        let addAmount = 0;
        actions.forEach(async function (row) {
            addAmount = new BigNumber(addAmount).plus(new BigNumber(row.amount));
        });

        ctx.status = 200;
        ctx.body = { 'status_code': 200, 'currBalance': addAmount };
        return;
    }
    // 监听用的
    async accountBalance() {
        const ctx = this.ctx;
        let userAddress = ctx.query.userAddrDeposit;
        let balance = ctx.query.amountDeposit;
        let event = ctx.query.event;
        web3 = new Web3(new HDWalletProvider(mnemonic, url));
        balance =  web3.utils.fromWei(balance, 'ether');//amount;
       
        // 没有就创建，有就更新balance
        await ctx.model.Account.findOrCreate({
            where: {userAddress: userAddress}, 
            defaults: {accountAddress: 123456789, userAddress: userAddress, balance: balance, freezeBalance: 0}
          })
          .spread(async (action, created) => {
            if(created === false) {
                const conn = await this.app.mysql.beginTransaction();
                // 下单的金额够不够用 不够直接catch，够了then的时候形成订单失败也会回滚 
                // 判断是加还是数据库是入金 分奖池 还是提现 
                if(event == "Withdraw") { 
                    await conn.query(`UPDATE accounts SET balance=balance-${balance} WHERE userAddress='${userAddress}'`).then( async result => {
                        await conn.commit(); // 提交事务
                        ctx.status = 201;
                        ctx.body = {'status_code':201, 'msg': "提现 成功！"};
                    })
                }else {
                    await conn.query(`UPDATE accounts SET balance=balance+${balance} WHERE userAddress='${userAddress}'`).then( async result => {
                        await conn.commit(); // 提交事务
                        ctx.status = 201;
                        ctx.body = {'status_code':201, 'msg': "balance 成功！"};
                    })
                }
            }
          })
        ctx.status = 201;
        ctx.body = { 'status_code': 201, 'data': '入金成功！' };
    }

    // 监听修改
    async updateAction() {
        const ctx = this.ctx;
        // let uuid = uuid;
        let constractUuid = ctx.query.uuid; // 这个应该是合约返回的值
        let allocation = ctx.query.allocation;
        const ActionCastRes = await ctx.model.Action.findOne({
            where: {
                uuid: constractUuid
            }
        });
        
        // 合约返回的时候还有一个特殊的值 用于判断是否成功 成功1 失败是2
        if(allocation == true) {
            await ActionCastRes.update({ ifFail:1 }); // ifFail 1 成功， 0没有提交 2 失败
        }else {
            await ActionCastRes.update({ ifFail:2 });
        }
        ctx.status = 201;
        ctx.body = { 'status_code': 201, 'data': '订单修改成功！' };
    }
  
}

module.exports = ActionController;